package pers.ree.demo.concurrent;

import java.util.concurrent.*;

public class ExecutorsDemo {

    public static void main(String[] args) {
//        testFixedThreadPool();

//        testSingleThreadExecutor();

//        testCachedThreadPool();

//        testSchedule();

//        testScheduleAtFixedRate();

        testScheduleWithFixedDelay();
    }

    /**
     * newCachedThreadPool 使用 SynchronousQueue 的主要原因确实与其不存储任务的特性有关，这个设计允许线程池能够快速地响应新任务。这里的关键是 SynchronousQueue 要求每个插入操作必须等待一个相应的移除操作，这意味着任务不能在队列中存储，必须立即被某个线程消费。这种“握手”（handoff）机制确保了以下几点：
     *
     * 直接任务移交：当新任务提交到线程池时，如果线程池中存在空闲线程，则该任务会直接交给空闲线程执行。这种直接移交减少了任务在队列中的等待时间，提高了响应速度。
     *
     * 按需创建线程：如果线程池中没有空闲线程，SynchronousQueue 的特性会导致线程池创建一个新的线程来处理这个任务。这允许线程池根据任务的到达动态地增加线程数量。
     *
     * 空闲线程回收：当线程池中的线程在指定的空闲时间（keepAliveTime）内没有任务执行时，这些线程会被回收。这有助于在任务负载减少时减少资源占用。
     *
     * 快速扩缩容：由于 SynchronousQueue 不存储任务，线程池可以快速地根据任务负载增加或减少线程数量，实现快速的扩缩容效果。
     */
    private static void testCachedThreadPool() {
        // 初始线程为0，最大线程为Integer.MAX_VALUE
        // 等待队列SynchronousQueue
        ExecutorService executorService = Executors.newCachedThreadPool();

        int threadNum = 3;
        int i = threadNum;
        // 启动3个线程
        while (i-- > 0) {
            execute(executorService);
        }

        int j = threadNum;
        // 启动3个线程
        while (j-- > 0) {
            System.out.println("in execute2");
            execute2(executorService);
        }
    }

    private static void testSingleThreadExecutor() {
        // // 初始线程为1，最大线程为1
        // 等待队列LinkedBlockingQueue
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        int i = 2;
        while (i-- > 0) {
            execute(executorService);
        }
    }

    private static void testFixedThreadPool() {
        int threadNum = 4;
        // 初始线程和最大线程都为threadNum
        // 等待队列LinkedBlockingQueue
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);

        int i = threadNum;
        while (i-- > 2) {
            execute(executorService);
        }

        int j = threadNum;
        while (j-- > 0) {
            System.out.println("in execute2");
            execute2(executorService);
        }
    }

    private static void testSchedule() {
        // 延迟 3 秒后执行一次任务
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        executor.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("In testSchedule, delayed task executed.");
            }
        }, 10, TimeUnit.SECONDS);

        executor.shutdown();

        System.out.println("testSchedule end......");
    }

    private static void testScheduleAtFixedRate() {
        // 延迟 1 秒后开始执行任务，每 2 秒执行一次
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("In testScheduleAtFixedRate, period task executed.");
            }
        }, 1, 2, TimeUnit.SECONDS);

        // 运行一段时间后关闭线程池
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        executor.shutdown();

        System.out.println("testScheduleAtFixedRate end......");
    }

    private static void testScheduleWithFixedDelay() {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        // 延迟 1 秒后开始执行任务，每次任务执行完等待 3 秒再执行下一次
        executor.scheduleWithFixedDelay(() -> {
            System.out.println("In testScheduleAtFixedRate, task with fixed delay executed.");
        }, 1, 3, TimeUnit.SECONDS);

        ThreadPoolExecutorDemo.printExecutorServiceInfoWithoutSleepTime(executor);

        // 运行一段时间后关闭线程池
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ThreadPoolExecutorDemo.printExecutorServiceInfoWithoutSleepTime(executor);

        executor.shutdown();
    }

    static void execute(ExecutorService executorService) {
        executorService.execute(() -> {
            int i = 0;
            while (true) {
                System.out.println(Thread.currentThread().getName() + ", i=" + ++i);
                try {
                    TimeUnit.MILLISECONDS.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    static void execute2(ExecutorService executorService) {
        executorService.execute(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + " running in execute2");
                try {
                    TimeUnit.MILLISECONDS.sleep(100L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}
